package website.lucaswork.survey.core.services.impls;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import website.lucaswork.survey.core.daos.UserRepository;
import website.lucaswork.survey.core.dtos.DtoFactory;
import website.lucaswork.survey.core.dtos.UserDTO;
import website.lucaswork.survey.core.entities.Authority;
import website.lucaswork.survey.core.entities.User;
import website.lucaswork.survey.core.enums.RoleEnum;
import website.lucaswork.survey.core.services.UserService;

import java.util.ArrayList;
import java.util.List;

@Transactional
@Service(value = "userService")
public class UserServiceImpl implements UserService {

    @Autowired
    UserRepository userRepo;

    @Autowired
    PasswordEncoder passwordEncoder;

    @Autowired
    DtoFactory dtoFactory;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User ret = userRepo.findByUsername(username);
        if (ret == null) {
            throw new UsernameNotFoundException("User with name " + username + " is not existed.");
        }
        return ret;
    }

    /**
     * This function is used in User Manage screen that main assign any role to a user.
     *
     * @param user
     * @param roles
     * @return
     */
    @Secured("ROLE_ADMIN")
    @Override
    public User updateUserRole(User user, RoleEnum[] roles) {
        // firstly remove all roles on this user
        user.setAuthorities(null);
        userRepo.save(user);
        if (roles != null || roles.length > 0) {
            for (RoleEnum role : roles) {
                Authority auth = new Authority();
                auth.setRole(role);
                auth.setUser(user);
                user.addAuthority(auth);
            }
        }
        return userRepo.save(user);
    }

    /**
     * This function is used in register screen, only assign USER Role to new created user.
     *
     * @param username
     * @param email
     * @param password
     * @return
     */
    @Override
    public User createUser(String username, String email, String password) {
        User user = new User();
        user.setUsername(username);
        user.setPasswordHash(passwordEncoder.encode(password));
        user.setEmail(email);
        user.setStatus(User.UserStatus.STATUS_ACTIVE);
        Authority auth = new Authority();
        auth.setRole(RoleEnum.ROLE_USER);
        auth.setUser(user);
        user.addAuthority(auth);
        return userRepo.save(user);
    }

    @Override
    public User findUserByEmail(String email) throws UsernameNotFoundException {
        User ret = userRepo.findByEmail(email);
        if (ret == null) {
            throw new UsernameNotFoundException("user with email " + email + " is not existed");
        }
        return ret;
    }

    @Override
    public User findUserByUsername(String username) throws UsernameNotFoundException {
        User ret = userRepo.findByUsername(username);
        if (ret == null) {
            throw new UsernameNotFoundException("user with username " + username + " is not existed");
        }
        return ret;
    }

    @Override
    public Page<UserDTO> getPageUsers(Pageable pageable) {
        Page<User> users = userRepo.findAll(pageable);
        List<UserDTO> ret = new ArrayList<UserDTO>();
        for (User user : users) {
            ret.add(dtoFactory.produceUserSummaryDTO(user));
        }
        return new PageImpl<UserDTO>(ret, pageable, users.getTotalElements());
    }
}
